import byteUtils from './byteUtils';
import { ByteUtils } from '../utils/ByteUtils';
import { SM2 } from './sm2';
import { SM3Digest } from './sm3';
import { SM4 } from './sm4';
import { systemDateTime } from '@kit.BasicServicesKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';

export class OfflineCodeUtils {
  static SEC_KEY = "T6SM2GenKey_bairiyishanjin";

  /**
   * 生码
   * 参数均为16进制字符串
   */
  async genCode(pubKeyHex, priKeyHex, devCodeHex, authDataHex) {
    const time = new Date()

    console.log("OfflineCode seckey = " + OfflineCodeUtils.SEC_KEY);
    console.log("OfflineCode devcode = " + devCodeHex);
    console.log("OfflineCode time = " + time);
    console.log("OfflineCode pubkey = " + pubKeyHex);
    console.log("OfflineCode prikey = " + priKeyHex);
    console.log("OfflineCode authData = " + authDataHex + " length = " + authDataHex.length / 2);
    //crc
    const crcStart = systemDateTime.getTime()
    const crc = this.dateAndDevCRC(byteUtils.hexStrToBytes(devCodeHex), time)
    console.log("OfflineCode crc = " + byteUtils.bytesToHexStr(crc).toUpperCase() + " length = " + crc.length);
    console.log('OfflineCode crcTime = ' + (systemDateTime.getTime() - crcStart))
    //签名
    const signStart = systemDateTime.getTime()
    var data = authDataHex + byteUtils.bytesToHexStr(crc);
    var sign = await this.sign(OfflineCodeUtils.SEC_KEY, pubKeyHex, priKeyHex, byteUtils.hexStrToBytes(data));
    console.log("OfflineCode sign = " + sign + " length = " + sign.length / 2);
    console.log('OfflineCode signTime = ' + (systemDateTime.getTime() - signStart))
    var code = data + sign;
    console.log("OfflineCode code = " + code + " length = " + code.length / 2);
    return code
  }

  genKeyPair(seckey) {
    //生成公私钥
    // var keyPair = SM2.generateKeyPair()
    var keyPair = this.convertSm2KeyPair(cryptoFramework.createAsyKeyGenerator('SM2_256').generateKeyPairSync())

    //压缩公钥
    var pubkey = SM2.pubKeyCompress(keyPair.ecpubhex)

    //加密私钥
    var secKeyBytes = byteUtils.stringToUtf8Bytes(seckey);
    var sm3 = new SM3Digest();
    sm3.update(secKeyBytes, 0, secKeyBytes.length);
    var secKeySM3 = sm3.doFinal();
    console.log("secKeySM3 = " + byteUtils.bytesToHexStr(secKeySM3));
    var priKey = SM4.cryptEcb_SM4(secKeySM3.slice(0, 16), byteUtils.hexStrToBytes(keyPair.ecprvhex), 1);

    console.log("pubkey = " + pubkey);
    console.log("priKey = " + byteUtils.bytesToHexStr(priKey));

    return {
      pubkeyFull: keyPair.ecpubhex.toUpperCase(),
      pubkey: pubkey.toUpperCase(),
      priKey: byteUtils.bytesToHexStr(priKey).toUpperCase()
    };
  };

  /**
   * 日期和设备ID CRC
   * @param devcode 设备ID，byte[]
   * @param time  时间，毫秒值
   * @returns byte[]
   */
  dateAndDevCRC(devcode, time) {
    //devcode crc
    var sm3 = new SM3Digest();
    sm3.update(devcode, 0, devcode.length);
    var bSm3HashData = sm3.doFinal();
    var devCodeCRC = 0;
    for (let i = 0; i < bSm3HashData.length; i++) {
      devCodeCRC ^= bSm3HashData[i];
    }

    //time
    var timeBytes = this.int64ToByte8(time / 1000);
    timeBytes = timeBytes.slice(4, timeBytes.length);

    //crc
    var crc = timeBytes[0] ^ timeBytes[1] ^ timeBytes[2] ^ timeBytes[3] ^ devCodeCRC;

    return [...timeBytes, crc];
  };

  /**
   * 签名
   * @param secKey 秘钥
   * @param pubkey 公钥
   * @param prikey 私钥
   * @param data 待签名数据 byte[]
   * @returns Hex字符串
   */
  async sign(secKey, pubkey, prikey, data) {
    var secKeyBytes = byteUtils.stringToUtf8Bytes(secKey);
    var sm3 = new SM3Digest();
    sm3.update(secKeyBytes, 0, secKeyBytes.length);
    var secKeySM3 = sm3.doFinal();
    console.log("secKeySM3 = " + byteUtils.bytesToHexStr(secKeySM3));

    const sm4Start = systemDateTime.getTime()
    var prikeyBytes = byteUtils.hexStrToBytes(prikey);
    var tmpPriKey = SM4.cryptEcb_SM4(secKeySM3.slice(0, 16), prikeyBytes, 0);
    console.log('OfflineCode sm4Time = ' + (systemDateTime.getTime() - sm4Start))

    const signWithSm3Start = systemDateTime.getTime()
    // var sign = SM2.signWithSM3(data, byteUtils.bytesToHexStr(tmpPriKey));

    let convertPriKey = await this.convertStrToPriKey(byteUtils.bytesToHexStr(tmpPriKey))
    // const signObj = cryptoFramework.createSign('SM2_256|SM3')
    // signObj.initSync(convertPriKey)
    // // signObj.update({data: new Uint8Array(data.slice(29))})
    // const dataBlob = signObj.signSync({ data: new Uint8Array(data) })

    const signData = this.sm2SignWithSM3(new Uint8Array(data),convertPriKey)
    let signRes = ByteUtils.bytesToHexStr(signData)
    signRes = this.d2i_SM2_SignText(signRes)

    console.log('OfflineCode signWithSm3Time = ' + (systemDateTime.getTime() - signWithSm3Start))
    return signRes.toUpperCase();
  };


  int64ToByte8(src) {
    var buffer = [];
    for (var i = 0; i < 8; ++i) {
      var offset = 64 - (i + 1) * 8;
      buffer[i] = (src >> offset & 255);
    }
    return buffer;
  }

  /**
   * 转换sm2秘钥
   * return: keyPair:{ecpubhex, ecprvhex} , hexString
   */
  convertSm2KeyPair(keyPair: cryptoFramework.KeyPair) {
    let pubKeyBytes = keyPair.pubKey.getEncoded().data
    let priKeyBytes = keyPair.priKey.getEncoded().data

    const pair = {
      ecpubhex: ByteUtils.bytesToHexStr(pubKeyBytes.slice(-64)),
      ecprvhex: ByteUtils.bytesToHexStr(priKeyBytes.slice(7, 7 + 32))
    }

    console.info('OfflineCode raw pubKey = ' + ByteUtils.bytesToHexStr(pubKeyBytes))
    console.info('OfflineCode pubKey = ' + pair.ecpubhex)
    console.info('OfflineCode compress pubKey = ' + SM2.pubKeyCompress(pair.ecpubhex))
    console.info('OfflineCode raw priKey = ' + ByteUtils.bytesToHexStr(priKeyBytes))
    console.info('OfflineCode priKey = ' + pair.ecprvhex)
    return pair
  }


  async convertStrToPriKey(keyStr: string): Promise<cryptoFramework.PriKey> {
    const sk = BigInt('0x' + keyStr)
    const priKeySpec: cryptoFramework.ECCPriKeySpec = {
      params: cryptoFramework.ECCKeyUtil.genECCCommonParamsSpec('NID_sm2'),
      sk: sk,
      algName: 'SM2',
      specType: cryptoFramework.AsyKeySpecType.PRIVATE_KEY_SPEC
    }
    const keypairGenerator = cryptoFramework.createAsyKeyGeneratorBySpec(priKeySpec)
    return await keypairGenerator.generatePriKey()
  }

  sm2SignWithSM3(data: Uint8Array, priKey: cryptoFramework.PriKey): Uint8Array {
    const signObj = cryptoFramework.createSign('SM2_256|SM3')
    signObj.initSync(priKey)
    // signObj.update({data: new Uint8Array(data.slice(29))})
    const dataBlob = signObj.signSync({ data: data })
    return dataBlob.data
  }

  // async encryptMessagePromise(publicKey: cryptoFramework.PubKey, plainText: cryptoFramework.DataBlob) {
  //     let cipher = cryptoFramework.createCipher('SM2_256|SM3');
  //     await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, publicKey, null);
  //     let encryptData = await cipher.doFinal(plainText);
  //     return encryptData;
  // }


  /*
  * 返回预处理后的数据
  **/
  d2i_SM2_SignText(standard_data: string): string {
    let randNum: string = "";
    let signText: string = "";
    let message = standard_data.slice("30".length, standard_data.length);
    let sequence_lenHex: string = this.getLenHex(message);
    message = message.slice(sequence_lenHex.length, message.length);

    try {
      let randNum_len: string = message.slice(0, 4);
      if (randNum_len == "0220") {
        randNum = message.slice(4, 4 + 64);
        message = message.slice(4 + 64, message.length);
      } else {
        randNum = message.slice(6, 6 + 64);
        message = message.slice(6 + 64, message.length);
      }

      let signText_len: string = message.slice(0, 4);
      if (signText_len == "0220") {
        signText = message.slice(4, 4 + 64);
      } else {
        signText = message.slice(6, 6 + 64);
      }
    } catch (e) {
      // hilog.error(0x1, "d2i_SM2_SignText", "SignText format is err");
    }
    return randNum + signText;
  }

  // 辅助函数：提取长度域的Hex字符串
  getLenHex(data: string): string {
    let byte: number = Number.parseInt("0x" + data.slice(0, 2));
    let len_size: number = byte > 127 ? byte - 0x80 + 1 : 1;
    return data.slice(0, len_size * 2);
  }
}

